package com.itfreer.bpm.activiti.workflow;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.itfreer.bpm.flow.api.IBpmWorkFlowImageService;
import com.itfreer.bpm.workflow.service.disposal.BpmWorkFlowDisposeQuery;
/**
 * 解析流程图图片工具功能
 * @author Administrator
 *
 */
@Component
public class ActivitiWorkFlowImageService implements IBpmWorkFlowImageService{
	
	/**
	 * 流程部署服务
	 */
	@Autowired(required = false)
	protected RepositoryService repositoryService;
	
	
	/**
	 * 
	 */
	@Autowired(required = false)
	protected ProcessEngineFactoryBean processEngine;
	
	/**
	 * 运行时服务
	 */
	@Autowired(required = false)
	private RuntimeService runtimeService;
	
	/**
	 * 流程历史服务
	 */
	@Autowired(required = false)
	protected HistoryService historyService;	
	
	/**
	 * 流程退件查询接口
	 */
	@Autowired(required = false)
	BpmWorkFlowDisposeQuery disposeQuery;
	
	
	/**
	 * 1、流程实例为空时返回定义流程图
	 * 2、流程挂起时，且项目已经退件的，返回流程定义流程图
	 * 3、流程实例查询不到时，返回流程定义流程图
	 */
	@Override
	public byte[] loadWorkFlowImage(String sexeid, String bpmKey) {
		if(sexeid==null || "".equals(sexeid.trim())) {
			return this.getDefinitionImage(bpmKey);
		}else {
			ProcessInstance processInst=runtimeService.createProcessInstanceQuery().processInstanceId(sexeid).singleResult();
			if(processInst==null) {
				return this.getDefinitionImage(bpmKey);
			}
			boolean isSuspended = processInst.isSuspended();
			if(isSuspended && this.disposeQuery.queryFlowDispose(sexeid)) {
				return this.getDefinitionImage(bpmKey);
			}
			return this.getActivitiProccessImage(sexeid);
		}
	}
	
	
	/**
	 * 生成流程定义图片
	 * 
	 * @param executionId
	 * @param response
	 * @param processInstance
	 */
	private byte[] getDefinitionImage(String pDefinitionId) {

		ProcessDefinition processDefinition = repositoryService
				.createProcessDefinitionQuery()
				.processDefinitionKey(pDefinitionId).latestVersion()
				.singleResult();
		String diagramResourceName = processDefinition.getDiagramResourceName();
		InputStream imageStream = repositoryService.getResourceAsStream(
				processDefinition.getDeploymentId(), diagramResourceName);

		// 输出资源内容到相应对象
		byte[] b = new byte[1024];
		int len;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();  
			while ((len = imageStream.read(b, 0, 1024)) != -1) {
				out.write(b, 0, len);
			}
			byte[] result= out.toByteArray();  
			out.close();
			imageStream.close();
			return result;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
    };

	
    /**
	 * @Author 保留流程6.0获取图片的方式
	 * @Note 
	 * @Date 
	 * @param processDefinitionId 流程定义ID
	 * @param resourceName        资源名称
	 */
	/*public void readResource(String processDefinitionId, String resourceName,String pProcessInstanceId, HttpServletResponse response)
	        throws Exception {
	    // 设置页面不缓存
	    response.setHeader("Pragma", "No-cache");
	    response.setHeader("Cache-Control", "no-cache");
	    response.setDateHeader("Expires", 0);
	
	    ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
	    ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();
	
	    if(resourceName.endsWith(".png") && pProcessInstanceId!=null)
	    {
	        getActivitiProccessImage(pProcessInstanceId,response);
	        //ProcessDiagramGenerator.generateDiagram(pde, "png", getRuntimeService().getActiveActivityIds(processInstanceId));
	    }
	    else
	    {
	        // 通过接口读取
	        InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);
	
	        // 输出资源内容到相应对象
	        byte[] b = new byte[1024];
	        int len = -1;
	        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
	            response.getOutputStream().write(b, 0, len);
	        }
	    }
	}*/
	
	/**
	 * 获取流程图像，已执行节点和流程线高亮显示
	 */
	public byte[] getActivitiProccessImage(String pProcessInstanceId)
	{
	    //logger.info("[开始]-获取流程图图像");
	    try {
	        //  获取历史流程实例
	        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
	                .processInstanceId(pProcessInstanceId).singleResult();

	        if (historicProcessInstance == null) {
	        	return null;
	        }
	        else
	        {
	            // 获取流程定义
	            ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
	                    .getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());

	            // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
	          /*  List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
	                    .processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();*/

	            // 已执行的节点ID集合
	          /*  List<String> executedActivityIdList = new ArrayList<String>();
	            int index = 1;
	            for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
	                index++;
	            }*/

	            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());

	            // 已执行的线集合
	            List<String> flowIds = new ArrayList<String>();
	            // 获取流程走过的线 (getHighLightedFlows是下面的方法)
	           // flowIds = getHighLightedFlows(bpmnModel,processDefinition, historicActivityInstanceList);



	            // 获取流程图图像字符流
	            ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
	            
	    		List<String> activeActivityIds = runtimeService
	    				.getActiveActivityIds(pProcessInstanceId);
	            
	            //配置字体
	    		InputStream imageStream = pec.generateDiagram(bpmnModel, "png", activeActivityIds, flowIds,"宋体","微软雅黑","黑体",null,2.0);
	            //InputStream imageStream = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds,"宋体","微软雅黑","黑体",null,2.0);
	        	ByteArrayOutputStream out = new ByteArrayOutputStream();  
	            int bytesRead = 0;
	            byte[] buffer = new byte[8192];
	            while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
	            	out.write(buffer, 0, bytesRead);
	            }
	            byte[] result=out.toByteArray();
	            out.close();
	            imageStream.close();
	            return result;
	        }
	    } catch (Exception e) {
	        System.out.println(e.getMessage());
	        return null;
	    }
	}
	
	
	/**
	 * 高亮显示已经通过的流程
	 * @param bpmnModel
	 * @param processDefinitionEntity
	 * @param historicActivityInstances
	 * @return
	 */
	public List<String> getHighLightedFlows(BpmnModel bpmnModel,ProcessDefinitionEntity processDefinitionEntity,List<HistoricActivityInstance> historicActivityInstances)
	{
	    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小时制
	    List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId

	    for (int i = 0; i < historicActivityInstances.size() - 1; i++)
	    {
	        // 对历史流程节点进行遍历
	        // 得到节点定义的详细信息
	        FlowNode activityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());


	        List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
	        FlowNode sameActivityImpl1 = null;

	        HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
	        HistoricActivityInstance activityImp2_ ;

	        for(int k = i + 1 ; k <= historicActivityInstances.size() - 1; k++)
	        {
	            activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点

	            if ( activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
	                    df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))   ) //都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
	            {

	            }
	            else
	            {
	                sameActivityImpl1 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到紧跟在后面的一个节点
	                break;
	            }

	        }
	        sameStartTimeNodes.add(sameActivityImpl1); // 将后面第一个节点放在时间相同节点的集合里
	        for (int j = i + 1; j < historicActivityInstances.size() - 1; j++)
	        {
	            HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
	            HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点

	            if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))  )
	            {// 如果第一个节点和第二个节点开始时间相同保存
	                FlowNode sameActivityImpl2 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
	                sameStartTimeNodes.add(sameActivityImpl2);
	            }
	            else
	            {// 有不相同跳出循环
	                break;
	            }
	        }
	        List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows() ; // 取出节点的所有出去的线

	        for (SequenceFlow pvmTransition : pvmTransitions)
	        {// 对所有的线进行遍历
	            FlowNode pvmActivityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement( pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
	            if (sameStartTimeNodes.contains(pvmActivityImpl)) {
	                highFlows.add(pvmTransition.getId());
	            }
	        }

	    }
	    return highFlows;

	}
}
